﻿using System;
using System.Linq;
using System.Web.Caching;
using System.Web;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using KLS.PPDS.Utility.Http;
using System.Collections;

namespace KLS.PPDS.Utility
{
    public class CacheManager<T>
    {
        private Cache Cache { get { return HttpRuntime.Cache; } }
        
        private string Key { get; set; }

        private string FileName { get; set; }
        private string FullFilePath { get { return HttpHelper.MapPath(Path.Combine(WebConfig.CachePath, FileName)); } }
        public T CachedObject { get; private set; }

        public CacheManager(string key)
        {
            Key = key;

            FileName = string.Format("{0}.npcache", this.Key);

            Initialize();
        }

        /// <summary>
        /// Initialization
        /// </summary>
        private void Initialize()
        {
            if(!Directory.Exists(HttpHelper.MapPath(WebConfig.CachePath)))
            { 
                Directory.CreateDirectory(HttpHelper.MapPath(WebConfig.CachePath));
            }

            string fileName = Convert.ToString(Cache[this.Key]);
            if (!string.IsNullOrWhiteSpace(fileName))
            {
                ReadCache();
            }            
        }

        /// <summary>
        /// Read from cache file and generate the object
        /// </summary>
        private void ReadCache()
        {
            try
            {
                if (File.Exists(FullFilePath))
                {
                    using (var ms = new MemoryStream(File.ReadAllBytes(FullFilePath)))
                    {
                        var formatter = new BinaryFormatter();
                        ms.Position = 0;
                        CachedObject = (T)formatter.Deserialize(ms);
                    }
                }
            }
            catch { }
        }

        /// <summary>
        /// Store object to cache
        /// </summary>
        /// <param name="value"></param>
        public void Store(T value)
        {
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, value);
                ms.Position = 0;
                var buffer = ms.ToArray();
                using (var fs = new FileStream(FullFilePath, FileMode.Create, FileAccess.Write))
                {
                    fs.Write(buffer, 0, buffer.Length);
                    fs.Flush();

                    Cache.Insert(Key, FileName, null, DateTime.Now.AddHours(WebConfig.CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Default, new CacheItemRemovedCallback(OnRemove));
                    CachedObject = value;
                }
            }
        }

        /// <summary>
        /// This method call when cache expire or remove.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="reason"></param>
        public static void OnRemove(string key, object value, CacheItemRemovedReason reason)
        {
            try
            {
                var path = Path.Combine(HttpRuntime.AppDomainAppPath, WebConfig.CachePath, Convert.ToString(value)).Replace("~/", "\\").Replace("\\\\", "\\");
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
            }

// ReSharper disable EmptyGeneralCatchClause
            catch (Exception)
// ReSharper restore EmptyGeneralCatchClause
            { }
        }

        /// <summary>
        /// Remove from cache
        /// </summary>
        public void Expire()
        {
            try
            {
                var keys = (from DictionaryEntry item in Cache select Convert.ToString(item.Key)).ToList();
                keys=keys.Where(x=> x.StartsWith(Key, StringComparison.OrdinalIgnoreCase)).ToList();
                foreach (var key in keys)
                {
                    Cache.Remove(key);
                }
            }
            catch { }
        }
    }
}
